跳到主要内容

渲染进程卡死检测、崩溃恢复与现场还原怎么做?

考察点​

  • unresponsive / render-process-gone 事件体系
  • 用户数据不丢失的恢复设计

参考答案​

1. 检测机制​

(1)系统事件(主进程监听)

// 渲染进程崩溃(段错误、OOM 被 kill)
win.webContents.on('render-process-gone', (e, details) => {
logger.error('渲染进程崩溃:', details.reason, details.exitCode);
// reason: clean-exit / abnormal-exit / killed / crashed / oom / ...
handleCrash(win, details);
});

// 页面无响应(JS 死循环、长任务阻塞)
win.on('unresponsive', () => {
logger.warn('窗口无响应');
showUnresponsiveDialog(win); // 询问用户"等待 or 重启"
});

win.on('responsive', () => {
logger.info('窗口恢复响应');
dismissUnresponsiveDialog();
});

(2)应用层心跳(更灵敏的补充)

系统事件触发较慢(Chromium 要等几十秒),对灵敏检测可加心跳:

// preload:每秒回应主进程 ping
ipcRenderer.on('health:ping', () => {
ipcRenderer.send('health:pong', Date.now());
});

// 主进程:超时未 pong 判定异常
setInterval(() => {
win.webContents.send('health:ping');
setTimeout(() => {
if (!pongReceived) handleHang(win);
}, 5000);
}, 10000);

2. 恢复策略​

(1)崩溃恢复:直接重载

function handleCrash(win, details) {
if (details.reason === 'oom') {
// OOM:提示用户减少数据量,重载可能再次 OOM
showOOMDialog();
}
// 大多数情况:重载恢复
win.reload();
}

(2)无响应处理:给用户选择权

// 弹原生对话框:等待 / 强制重启页面
const { response } = await dialog.showMessageBox({
message: '页面无响应',
buttons: ['继续等待', '重新加载'],
});
if (response === 1) win.reload();

(3)崩溃循环防护

let crashCount = 0;
win.webContents.on('render-process-gone', () => {
crashCount++;
if (crashCount > 3) {
// 短时间内反复崩溃:进入安全模式(禁用插件/清空状态/回退首页)
enterSafeMode(win);
} else {
win.reload();
}
});

3. 现场还原(崩溃后用户数据不丢)​

(1)草稿自动保存(编辑器类核心)

// 渲染进程:内容变更防抖持久化
const saveDraft = debounce((content) => {
// 优先 localStorage(同步、崩溃瞬间也可能保住)
localStorage.setItem('draft:doc-1', JSON.stringify({
content,
savedAt: Date.now(),
}));
}, 1000);

editor.on('change', saveDraft);

(2)关键状态定期快照

// 每 30s 把工作区状态快照发给主进程落盘
setInterval(() => {
window.api.saveWorkspaceSnapshot(collectWorkspaceState());
}, 30_000);

(3)崩溃后恢复提示

// 页面重载后检测草稿
onMounted(() => {
const draft = localStorage.getItem('draft:doc-1');
if (draft && isNewerThanServer(draft)) {
showRestoreBanner({
onRestore: () => restoreDraft(JSON.parse(draft)),
onDiscard: () => localStorage.removeItem('draft:doc-1'),
});
}
});

4. 日志与归因​

崩溃恢复的同时要留证据(见「崩溃日志收集」):

  • render-process-gone 的 details(reason/exitCode)随崩溃上报
  • 崩溃前操作路径(用户行为埋点的最后 N 条)帮助复现
  • OOM 类崩溃附带内存水位数据

5. 常见卡死原因速查​

原因特征
JS 死循环CPU 100%,unresponsive 触发
同步大 IPC/大对象克隆操作特定功能时卡
内存膨胀触发频繁 GC越来越卡直到 OOM
GPU 进程问题花屏/白屏伴随卡死
主进程阻塞拖累所有窗口同时卡(不是单个窗口卡)

6. 面试话术​

检测双轨:系统事件(render-process-gone 管崩溃、unresponsive 管卡死)+ 应用层心跳(更灵敏)。恢复分级:崩溃直接 reload、无响应给用户等待/重载选择权、短时间反复崩溃进安全模式。现场还原靠防抖草稿(localStorage 优先)+ 定期快照到主进程,重载后提供恢复横幅。所有崩溃都带 details 和操作轨迹上报,形成归因闭环。

常见追问​

  • 重载会丢路由状态吗? SPA 配合 URL 路由可恢复到原页面;复杂工作区状态靠快照恢复。
  • 主进程卡死呢? 主进程无 unresponsive 类事件可自救,靠外部 watchdog(见「主进程异常捕获与崩溃守护」),所以主进程要极度克制不做重活。